'use strict';

// Last Updated On: 2020-03-23 2:31:22 AM UTC

// ________________
// DetectRTC v1.4.0

// Open-Sourced: https://github.com/muaz-khan/DetectRTC

// --------------------------------------------------
// Muaz Khan     - www.MuazKhan.com
// MIT License   - www.WebRTC-Experiment.com/licence
// --------------------------------------------------

(function() {
  var browserFakeUserAgent = 'Fake/5.0 (FakeOS) AppleWebKit/123 (KHTML, like Gecko) Fake/12.3.4567.89 Fake/123.45'

  var isNodejs = typeof process === 'object' && typeof process.versions === 'object' && process.versions.node && /* node-process*/
		!process.browser
  if (isNodejs) {
    var version = process.versions.node.toString().replace('v', '')
    browserFakeUserAgent = 'Nodejs/' + version + ' (NodeOS) AppleWebKit/' + version + ' (KHTML, like Gecko) Nodejs/' +
			version + ' Nodejs/' + version
  }

  (function(that) {
    if (typeof window !== 'undefined') {
      return
    }

    if (typeof window === 'undefined' && typeof global !== 'undefined') {
      global.navigator = {
        userAgent: browserFakeUserAgent,
        getUserMedia: function() {}
      }

      that.window = global
    } else if (typeof window === 'undefined') {
      // window = this;
    }

    if (typeof location === 'undefined') {
      that.location = {
        protocol: 'file:',
        href: '',
        hash: ''
      }
    }

    if (typeof screen === 'undefined') {
      that.screen = {
        width: 0,
        height: 0
      }
    }
  })(typeof global !== 'undefined' ? global : window)

  var navigator = window.navigator

  if (typeof navigator !== 'undefined') {
    if (typeof navigator.webkitGetUserMedia !== 'undefined') {
      navigator.getUserMedia = navigator.webkitGetUserMedia
    }

    if (typeof navigator.mozGetUserMedia !== 'undefined') {
      navigator.getUserMedia = navigator.mozGetUserMedia
    }
  } else {
    navigator = {
      getUserMedia: function() {},
      userAgent: browserFakeUserAgent
    }
  }

  var isMobileDevice = !!(/Android|webOS|iPhone|iPad|iPod|BB10|BlackBerry|IEMobile|Opera Mini|Mobile|mobile/i.test(
    navigator.userAgent || ''))

  var isEdge = navigator.userAgent.indexOf('Edge') !== -1 && (!!navigator.msSaveOrOpenBlob || !!navigator.msSaveBlob)

  var isOpera = !!window.opera || navigator.userAgent.indexOf(' OPR/') >= 0
  var isFirefox = navigator.userAgent.toLowerCase().indexOf('firefox') > -1 && ('netscape' in window) && / rv:/.test(
    navigator.userAgent)
  var isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent)
  var isChrome = !!window.chrome && !isOpera
  var isIE = typeof document !== 'undefined' && !!document.documentMode && !isEdge

  // this one can also be used:
  // https://www.websocket.org/js/stuff.js (DetectBrowser.js)

  function getBrowserInfo() {
    var nAgt = navigator.userAgent
    var browserName = navigator.appName
    var fullVersion = '' + parseFloat(navigator.appVersion)
    var majorVersion = parseInt(navigator.appVersion, 10)
    var nameOffset, verOffset, ix

    // In Opera, the true version is after 'Opera' or after 'Version'
    if (isOpera) {
      browserName = 'Opera'
      try {
        fullVersion = navigator.userAgent.split('OPR/')[1].split(' ')[0]
        majorVersion = fullVersion.split('.')[0]
      } catch (e) {
        fullVersion = '0.0.0.0'
        majorVersion = 0
      }
    } else if (isIE) {
      // In MSIE version <=10, the true version is after 'MSIE' in userAgent
      // In IE 11, look for the string after 'rv:'
      verOffset = nAgt.indexOf('rv:')
      if (verOffset > 0) { // IE 11
        fullVersion = nAgt.substring(verOffset + 3)
      } else { // IE 10 or earlier
        verOffset = nAgt.indexOf('MSIE')
        fullVersion = nAgt.substring(verOffset + 5)
      }
      browserName = 'IE'
    } else if (isChrome) {
      // In Chrome, the true version is after 'Chrome'
      verOffset = nAgt.indexOf('Chrome')
      browserName = 'Chrome'
      fullVersion = nAgt.substring(verOffset + 7)
    } else if (isSafari) {
      // In Safari, the true version is after 'Safari' or after 'Version'
      // both and safri and chrome has same userAgent
      if (nAgt.indexOf('CriOS') !== -1) {
        verOffset = nAgt.indexOf('CriOS')
        browserName = 'Chrome'
        fullVersion = nAgt.substring(verOffset + 6)
      } else if (nAgt.indexOf('FxiOS') !== -1) {
        verOffset = nAgt.indexOf('FxiOS')
        browserName = 'Firefox'
        fullVersion = nAgt.substring(verOffset + 6)
      } else {
        verOffset = nAgt.indexOf('Safari')

        browserName = 'Safari'
        fullVersion = nAgt.substring(verOffset + 7)

        if ((verOffset = nAgt.indexOf('Version')) !== -1) {
          fullVersion = nAgt.substring(verOffset + 8)
        }

        if (navigator.userAgent.indexOf('Version/') !== -1) {
          fullVersion = navigator.userAgent.split('Version/')[1].split(' ')[0]
        }
      }
    } else if (isFirefox) {
      // In Firefox, the true version is after 'Firefox'
      verOffset = nAgt.indexOf('Firefox')
      browserName = 'Firefox'
      fullVersion = nAgt.substring(verOffset + 8)
    } else if ((nameOffset = nAgt.lastIndexOf(' ') + 1) < (verOffset = nAgt.lastIndexOf('/'))) {
      // In most other browsers, 'name/version' is at the end of userAgent
      browserName = nAgt.substring(nameOffset, verOffset)
      fullVersion = nAgt.substring(verOffset + 1)

      if (browserName.toLowerCase() === browserName.toUpperCase()) {
        browserName = navigator.appName
      }
    }

    if (isEdge) {
      browserName = 'Edge'
      fullVersion = navigator.userAgent.split('Edge/')[1]
      // fullVersion = parseInt(navigator.userAgent.match(/Edge\/(\d+).(\d+)$/)[2], 10).toString();
    }

    // trim the fullVersion string at semicolon/space/bracket if present
    if ((ix = fullVersion.search(/[; \)]/)) !== -1) {
      fullVersion = fullVersion.substring(0, ix)
    }

    majorVersion = parseInt('' + fullVersion, 10)

    if (isNaN(majorVersion)) {
      fullVersion = '' + parseFloat(navigator.appVersion)
      majorVersion = parseInt(navigator.appVersion, 10)
    }

    return {
      fullVersion: fullVersion,
      version: majorVersion,
      name: browserName,
      isPrivateBrowsing: false
    }
  }

  // via: https://gist.github.com/cou929/7973956

  function retry(isDone, next) {
    var currentTrial = 0
    var maxRetry = 50
    var isTimeout = false
    var id = window.setInterval(
      function() {
        if (isDone()) {
          window.clearInterval(id)
          next(isTimeout)
        }
        if (currentTrial++ > maxRetry) {
          window.clearInterval(id)
          isTimeout = true
          next(isTimeout)
        }
      },
      10
    )
  }

  function isIE10OrLater(userAgent) {
    var ua = userAgent.toLowerCase()
    if (ua.indexOf('msie') === 0 && ua.indexOf('trident') === 0) {
      return false
    }
    var match = /(?:msie|rv:)\s?([\d\.]+)/.exec(ua)
    if (match && parseInt(match[1], 10) >= 10) {
      return true
    }
    return false
  }

  function detectPrivateMode(callback) {
    var isPrivate

    try {
      if (window.webkitRequestFileSystem) {
        window.webkitRequestFileSystem(
          window.TEMPORARY, 1,
          function() {
            isPrivate = false
          },
          function(e) {
            isPrivate = true
          }
        )
      } else if (window.indexedDB && /Firefox/.test(window.navigator.userAgent)) {
        var db
        try {
          db = window.indexedDB.open('test')
          db.onerror = function() {
            return true
          }
        } catch (e) {
          isPrivate = true
        }

        if (typeof isPrivate === 'undefined') {
          retry(
            function isDone() {
              return db.readyState === 'done'
            },
            function next(isTimeout) {
              if (!isTimeout) {
                isPrivate = !db.result
              }
            }
          )
        }
      } else if (isIE10OrLater(window.navigator.userAgent)) {
        isPrivate = false
        try {
          if (!window.indexedDB) {
            isPrivate = true
          }
        } catch (e) {
          isPrivate = true
        }
      } else if (window.localStorage && /Safari/.test(window.navigator.userAgent)) {
        try {
          window.localStorage.setItem('test', 1)
        } catch (e) {
          isPrivate = true
        }

        if (typeof isPrivate === 'undefined') {
          isPrivate = false
          window.localStorage.removeItem('test')
        }
      }
    } catch (e) {
      isPrivate = false
    }

    retry(
      function isDone() {
        return typeof isPrivate !== 'undefined'
      },
      function next(isTimeout) {
        callback(isPrivate)
      }
    )
  }

  var isMobile = {
    Android: function() {
      return navigator.userAgent.match(/Android/i)
    },
    BlackBerry: function() {
      return navigator.userAgent.match(/BlackBerry|BB10/i)
    },
    iOS: function() {
      return navigator.userAgent.match(/iPhone|iPad|iPod/i)
    },
    Opera: function() {
      return navigator.userAgent.match(/Opera Mini/i)
    },
    Windows: function() {
      return navigator.userAgent.match(/IEMobile/i)
    },
    any: function() {
      return (isMobile.Android() || isMobile.BlackBerry() || isMobile.iOS() || isMobile.Opera() || isMobile.Windows())
    },
    getOsName: function() {
      var osName = 'Unknown OS'
      if (isMobile.Android()) {
        osName = 'Android'
      }

      if (isMobile.BlackBerry()) {
        osName = 'BlackBerry'
      }

      if (isMobile.iOS()) {
        osName = 'iOS'
      }

      if (isMobile.Opera()) {
        osName = 'Opera Mini'
      }

      if (isMobile.Windows()) {
        osName = 'Windows'
      }

      return osName
    }
  }

  // via: http://jsfiddle.net/ChristianL/AVyND/
  function detectDesktopOS() {
    var unknown = '-'

    var nVer = navigator.appVersion
    var nAgt = navigator.userAgent

    var os = unknown
    var clientStrings = [{
      s: 'Chrome OS',
      r: /CrOS/
    }, {
      s: 'Windows 10',
      r: /(Windows 10.0|Windows NT 10.0)/
    }, {
      s: 'Windows 8.1',
      r: /(Windows 8.1|Windows NT 6.3)/
    }, {
      s: 'Windows 8',
      r: /(Windows 8|Windows NT 6.2)/
    }, {
      s: 'Windows 7',
      r: /(Windows 7|Windows NT 6.1)/
    }, {
      s: 'Windows Vista',
      r: /Windows NT 6.0/
    }, {
      s: 'Windows Server 2003',
      r: /Windows NT 5.2/
    }, {
      s: 'Windows XP',
      r: /(Windows NT 5.1|Windows XP)/
    }, {
      s: 'Windows 2000',
      r: /(Windows NT 5.0|Windows 2000)/
    }, {
      s: 'Windows ME',
      r: /(Win 9x 4.90|Windows ME)/
    }, {
      s: 'Windows 98',
      r: /(Windows 98|Win98)/
    }, {
      s: 'Windows 95',
      r: /(Windows 95|Win95|Windows_95)/
    }, {
      s: 'Windows NT 4.0',
      r: /(Windows NT 4.0|WinNT4.0|WinNT|Windows NT)/
    }, {
      s: 'Windows CE',
      r: /Windows CE/
    }, {
      s: 'Windows 3.11',
      r: /Win16/
    }, {
      s: 'Android',
      r: /Android/
    }, {
      s: 'Open BSD',
      r: /OpenBSD/
    }, {
      s: 'Sun OS',
      r: /SunOS/
    }, {
      s: 'Linux',
      r: /(Linux|X11)/
    }, {
      s: 'iOS',
      r: /(iPhone|iPad|iPod)/
    }, {
      s: 'Mac OS X',
      r: /Mac OS X/
    }, {
      s: 'Mac OS',
      r: /(MacPPC|MacIntel|Mac_PowerPC|Macintosh)/
    }, {
      s: 'QNX',
      r: /QNX/
    }, {
      s: 'UNIX',
      r: /UNIX/
    }, {
      s: 'BeOS',
      r: /BeOS/
    }, {
      s: 'OS/2',
      r: /OS\/2/
    }, {
      s: 'Search Bot',
      r: /(nuhk|Googlebot|Yammybot|Openbot|Slurp|MSNBot|Ask Jeeves\/Teoma|ia_archiver)/
    }]
    for (var i = 0, cs; i < clientStrings.length; i++) {
      cs = clientStrings[i]
      if (cs.r.test(nAgt)) {
        os = cs.s
        break
      }
    }

    var osVersion = unknown

    if (/Windows/.test(os)) {
      if (/Windows (.*)/.test(os)) {
        osVersion = /Windows (.*)/.exec(os)[1]
      }
      os = 'Windows'
    }

    switch (os) {
      case 'Mac OS X':
        if (/Mac OS X (10[\.\_\d]+)/.test(nAgt)) {
          osVersion = /Mac OS X (10[\.\_\d]+)/.exec(nAgt)[1]
        }
        break
      case 'Android':
        if (/Android ([\.\_\d]+)/.test(nAgt)) {
          osVersion = /Android ([\.\_\d]+)/.exec(nAgt)[1]
        }
        break
      case 'iOS':
        if (/OS (\d+)_(\d+)_?(\d+)?/.test(nAgt)) {
          osVersion = /OS (\d+)_(\d+)_?(\d+)?/.exec(nVer)
          osVersion = osVersion[1] + '.' + osVersion[2] + '.' + (osVersion[3] | 0)
        }
        break
    }

    return {
      osName: os,
      osVersion: osVersion
    }
  }

  var osName = 'Unknown OS'
  var osVersion = 'Unknown OS Version'

  function getAndroidVersion(ua) {
    ua = (ua || navigator.userAgent).toLowerCase()
    var match = ua.match(/android\s([0-9\.]*)/)
    return match ? match[1] : false
  }

  var osInfo = detectDesktopOS()

  if (osInfo && osInfo.osName && osInfo.osName !== '-') {
    osName = osInfo.osName
    osVersion = osInfo.osVersion
  } else if (isMobile.any()) {
    osName = isMobile.getOsName()

    if (osName === 'Android') {
      osVersion = getAndroidVersion()
    }
  }

  isNodejs = typeof process === 'object' && typeof process.versions === 'object' && process.versions.node

  if (osName === 'Unknown OS' && isNodejs) {
    osName = 'Nodejs'
    osVersion = process.versions.node.toString().replace('v', '')
  }

  var isCanvasSupportsStreamCapturing = false
  var isVideoSupportsStreamCapturing = false;
  ['captureStream', 'mozCaptureStream', 'webkitCaptureStream'].forEach(function(item) {
    if (typeof document === 'undefined' || typeof document.createElement !== 'function') {
      return
    }

    if (!isCanvasSupportsStreamCapturing && item in document.createElement('canvas')) {
      isCanvasSupportsStreamCapturing = true
    }

    if (!isVideoSupportsStreamCapturing && item in document.createElement('video')) {
      isVideoSupportsStreamCapturing = true
    }
  })

  var regexIpv4Local = /^(192\.168\.|169\.254\.|10\.|172\.(1[6-9]|2\d|3[01]))/
  var regexIpv4 = /([0-9]{1,3}(\.[0-9]{1,3}){3})/
  var regexIpv6 = /[a-f0-9]{1,4}(:[a-f0-9]{1,4}){7}/

  // via: https://github.com/diafygi/webrtc-ips
  function DetectLocalIPAddress(callback, stream) {
    if (!DetectRTC.isWebRTCSupported) {
      return
    }

    var isPublic = true
    var isIpv4 = true
    getIPs(function(ip) {
      if (!ip) {
        callback() // Pass nothing to tell that ICE-gathering-ended
      } else if (ip.match(regexIpv4Local)) {
        isPublic = false
        callback('Local: ' + ip, isPublic, isIpv4)
      } else if (ip.match(regexIpv6)) { // via https://ourcodeworld.com/articles/read/257/how-to-get-the-client-ip-address-with-javascript-only
        isIpv4 = false
        callback('Public: ' + ip, isPublic, isIpv4)
      } else {
        callback('Public: ' + ip, isPublic, isIpv4)
      }
    }, stream)
  }

  function getIPs(callback, stream) {
    if (typeof document === 'undefined' || typeof document.getElementById !== 'function') {
      return
    }

    var ipDuplicates = {}

    var RTCPeerConnection = window.RTCPeerConnection || window.mozRTCPeerConnection || window.webkitRTCPeerConnection

    if (!RTCPeerConnection) {
      var iframe = document.getElementById('iframe')
      if (!iframe) {
        return
      }
      var win = iframe.contentWindow
      RTCPeerConnection = win.RTCPeerConnection || win.mozRTCPeerConnection || win.webkitRTCPeerConnection
    }

    if (!RTCPeerConnection) {
      return
    }

    var peerConfig = null

    if (DetectRTC.browser === 'Chrome' && DetectRTC.browser.version < 58) {
      // todo: add support for older Opera
      peerConfig = {
        optional: [{
          RtpDataChannels: true
        }]
      }
    }

    var servers = {
      iceServers: [{
        urls: 'stun:stun.l.google.com:19302'
      }]
    }

    var pc = new RTCPeerConnection(servers, peerConfig)

    if (stream) {
      if (pc.addStream) {
        pc.addStream(stream)
      } else if (pc.addTrack && stream.getTracks()[0]) {
        pc.addTrack(stream.getTracks()[0], stream)
      }
    }

    function handleCandidate(candidate) {
      if (!candidate) {
        callback() // Pass nothing to tell that ICE-gathering-ended
        return
      }

      var match = regexIpv4.exec(candidate)
      if (!match) {
        return
      }
      var ipAddress = match[1]
      var isPublic = (candidate.match(regexIpv4Local))
      var isIpv4 = true

      if (ipDuplicates[ipAddress] === undefined) {
        callback(ipAddress, isPublic, isIpv4)
      }

      ipDuplicates[ipAddress] = true
    }

    // listen for candidate events
    pc.onicecandidate = function(event) {
      if (event.candidate && event.candidate.candidate) {
        handleCandidate(event.candidate.candidate)
      } else {
        handleCandidate() // Pass nothing to tell that ICE-gathering-ended
      }
    }

    // create data channel
    if (!stream) {
      try {
        pc.createDataChannel('sctp', {})
      } catch (e) {
        console.log(e)
      }
    }

    // create an offer sdp
    if (DetectRTC.isPromisesSupported) {
      pc.createOffer().then(function(result) {
        pc.setLocalDescription(result).then(afterCreateOffer)
      })
    } else {
      pc.createOffer(function(result) {
        pc.setLocalDescription(result, afterCreateOffer, function() {})
      }, function() {})
    }

    function afterCreateOffer() {
      var lines = pc.localDescription.sdp.split('\n')

      lines.forEach(function(line) {
        if (line && line.indexOf('a=candidate:') === 0) {
          handleCandidate(line)
        }
      })
    }
  }

  var MediaDevices = []

  var audioInputDevices = []
  var audioOutputDevices = []
  var videoInputDevices = []

  if (navigator.mediaDevices && navigator.mediaDevices.enumerateDevices) {
    // Firefox 38+ seems having support of enumerateDevices
    // Thanks @xdumaine/enumerateDevices
    navigator.enumerateDevices = function(callback) {
      var enumerateDevices = navigator.mediaDevices.enumerateDevices()
      if (enumerateDevices && enumerateDevices.then) {
        navigator.mediaDevices.enumerateDevices().then(callback).catch(function() {
          callback([])
        })
      } else {
        callback([])
      }
    }
  }

  // Media Devices detection
  var canEnumerate = false

  if (typeof MediaStreamTrack !== 'undefined' && 'getSources' in MediaStreamTrack) {
    canEnumerate = true
  } else if (navigator.mediaDevices && !!navigator.mediaDevices.enumerateDevices) {
    canEnumerate = true
  }

  var hasMicrophone = false
  var hasSpeakers = false
  var hasWebcam = false

  var isWebsiteHasMicrophonePermissions = false
  var isWebsiteHasWebcamPermissions = false

  // http://dev.w3.org/2011/webrtc/editor/getusermedia.html#mediadevices
  function checkDeviceSupport(callback) {
    if (!canEnumerate) {
      if (callback) {
        callback()
      }
      return
    }

    if (!navigator.enumerateDevices && window.MediaStreamTrack && window.MediaStreamTrack.getSources) {
      navigator.enumerateDevices = window.MediaStreamTrack.getSources.bind(window.MediaStreamTrack)
    }

    if (!navigator.enumerateDevices && navigator.enumerateDevices) {
      navigator.enumerateDevices = navigator.enumerateDevices.bind(navigator)
    }

    if (!navigator.enumerateDevices) {
      if (callback) {
        callback()
      }
      return
    }

    MediaDevices = []

    audioInputDevices = []
    audioOutputDevices = []
    videoInputDevices = []

    hasMicrophone = false
    hasSpeakers = false
    hasWebcam = false

    isWebsiteHasMicrophonePermissions = false
    isWebsiteHasWebcamPermissions = false

    // to prevent duplication
    var alreadyUsedDevices = {}

    navigator.enumerateDevices(function(devices) {
      // console.log(devices);
      MediaDevices = []

      audioInputDevices = []
      audioOutputDevices = []
      videoInputDevices = []
      // console.log('enumerateDevices');
      // console.log(devices);
      devices.forEach(function(_device) {
        var device = {}
        for (var d in _device) {
          try {
            if (typeof _device[d] !== 'function') {
              device[d] = _device[d]
            }
          } catch (e) {
            console.log(e)
          }
        }

        if (alreadyUsedDevices[device.deviceId + device.label + device.kind]) {
          return
        }

        // if it is MediaStreamTrack.getSources
        if (device.kind === 'audio') {
          device.kind = 'audioinput'
        }

        if (device.kind === 'video') {
          device.kind = 'videoinput'
        }

        if (!device.deviceId) {
          device.deviceId = device.id
        }

        if (!device.id) {
          device.id = device.deviceId
        }

        if (!device.label) {
          device.isCustomLabel = true

          if (device.kind === 'videoinput') {
            device.label = 'Camera ' + (videoInputDevices.length + 1)
          } else if (device.kind === 'audioinput') {
            device.label = 'Microphone ' + (audioInputDevices.length + 1)
          } else if (device.kind === 'audiooutput') {
            device.label = 'Speaker ' + (audioOutputDevices.length + 1)
          } else {
            device.label = 'Please invoke getUserMedia once.'
          }

          if (typeof DetectRTC !== 'undefined' && DetectRTC.browser.isChrome && DetectRTC.browser.version >= 46 && !/^(https:|chrome-extension:)$/g.test(location.protocol || '')) {
            if (typeof document !== 'undefined' && typeof document.domain === 'string' && document.domain.search &&
							document.domain.search(/localhost|127.0./g) === -1) {
              device.label = 'HTTPs is required to get label of this ' + device.kind + ' device.'
            }
          }
        } else {
          // Firefox on Android still returns empty label
          if (device.kind === 'videoinput' && !isWebsiteHasWebcamPermissions) {
            isWebsiteHasWebcamPermissions = true
          }

          if (device.kind === 'audioinput' && !isWebsiteHasMicrophonePermissions) {
            isWebsiteHasMicrophonePermissions = true
          }
        }

        if (device.kind === 'audioinput') {
          hasMicrophone = true

          if (audioInputDevices.indexOf(device) === -1) {
            audioInputDevices.push(device)
          }
        }

        if (device.kind === 'audiooutput') {
          hasSpeakers = true

          if (audioOutputDevices.indexOf(device) === -1) {
            audioOutputDevices.push(device)
          }
        }

        if (device.kind === 'videoinput') {
          hasWebcam = true

          if (videoInputDevices.indexOf(device) === -1) {
            videoInputDevices.push(device)
          }
        }

        // there is no 'videoouput' in the spec.
        MediaDevices.push(device)

        alreadyUsedDevices[device.deviceId + device.label + device.kind] = device
      })

      if (typeof DetectRTC !== 'undefined') {
        // to sync latest outputs
        DetectRTC.MediaDevices = MediaDevices
        DetectRTC.hasMicrophone = hasMicrophone
        DetectRTC.hasSpeakers = hasSpeakers
        DetectRTC.hasWebcam = hasWebcam

        DetectRTC.isWebsiteHasWebcamPermissions = isWebsiteHasWebcamPermissions
        DetectRTC.isWebsiteHasMicrophonePermissions = isWebsiteHasMicrophonePermissions

        DetectRTC.audioInputDevices = audioInputDevices
        DetectRTC.audioOutputDevices = audioOutputDevices
        DetectRTC.videoInputDevices = videoInputDevices
      }

      if (callback) {
        callback()
      }
    })
  }

  var DetectRTC = window.DetectRTC || {}

  // ----------
  // DetectRTC.browser.name || DetectRTC.browser.version || DetectRTC.browser.fullVersion
  DetectRTC.browser = getBrowserInfo()

  detectPrivateMode(function(isPrivateBrowsing) {
    DetectRTC.browser.isPrivateBrowsing = !!isPrivateBrowsing
  })

  // DetectRTC.isChrome || DetectRTC.isFirefox || DetectRTC.isEdge
  DetectRTC.browser['is' + DetectRTC.browser.name] = true

  // -----------
  DetectRTC.osName = osName
  DetectRTC.osVersion = osVersion

  // --------- Detect if system supports WebRTC 1.0 or WebRTC 1.1.
  var isWebRTCSupported = false;
  ['RTCPeerConnection', 'webkitRTCPeerConnection', 'mozRTCPeerConnection', 'RTCIceGatherer'].forEach(function(item) {
    if (isWebRTCSupported) {
      return
    }

    if (item in window) {
      isWebRTCSupported = true
    }
  })
  DetectRTC.isWebRTCSupported = isWebRTCSupported

  // -------
  DetectRTC.isORTCSupported = typeof RTCIceGatherer !== 'undefined'

  // --------- Detect if system supports screen capturing API
  var isScreenCapturingSupported = false
  if (DetectRTC.browser.isChrome && DetectRTC.browser.version >= 35) {
    isScreenCapturingSupported = true
  } else if (DetectRTC.browser.isFirefox && DetectRTC.browser.version >= 34) {
    isScreenCapturingSupported = true
  } else if (DetectRTC.browser.isEdge && DetectRTC.browser.version >= 17) {
    isScreenCapturingSupported = true
  } else if (DetectRTC.osName === 'Android' && DetectRTC.browser.isChrome) {
    isScreenCapturingSupported = true
  }

  if (!!navigator.getDisplayMedia || (navigator.mediaDevices && navigator.mediaDevices.getDisplayMedia)) {
    isScreenCapturingSupported = true
  }

  if (!/^(https:|chrome-extension:)$/g.test(location.protocol || '')) {
    var isNonLocalHost = typeof document !== 'undefined' && typeof document.domain === 'string' && document.domain.search &&
			document.domain.search(/localhost|127.0./g) === -1
    if (isNonLocalHost && (DetectRTC.browser.isChrome || DetectRTC.browser.isEdge || DetectRTC.browser.isOpera)) {
      isScreenCapturingSupported = false
    } else if (DetectRTC.browser.isFirefox) {
      isScreenCapturingSupported = false
    }
  }
  DetectRTC.isScreenCapturingSupported = isScreenCapturingSupported

  // --------- Detect if WebAudio API are supported
  var webAudio = {
    isSupported: false,
    isCreateMediaStreamSourceSupported: false
  };

  ['AudioContext', 'webkitAudioContext', 'mozAudioContext', 'msAudioContext'].forEach(function(item) {
    if (webAudio.isSupported) {
      return
    }

    if (item in window) {
      webAudio.isSupported = true

      if (window[item] && 'createMediaStreamSource' in window[item].prototype) {
        webAudio.isCreateMediaStreamSourceSupported = true
      }
    }
  })
  DetectRTC.isAudioContextSupported = webAudio.isSupported
  DetectRTC.isCreateMediaStreamSourceSupported = webAudio.isCreateMediaStreamSourceSupported

  // ---------- Detect if SCTP/RTP channels are supported.

  var isRtpDataChannelsSupported = false
  if (DetectRTC.browser.isChrome && DetectRTC.browser.version > 31) {
    isRtpDataChannelsSupported = true
  }
  DetectRTC.isRtpDataChannelsSupported = isRtpDataChannelsSupported

  var isSCTPSupportd = false
  if (DetectRTC.browser.isFirefox && DetectRTC.browser.version > 28) {
    isSCTPSupportd = true
  } else if (DetectRTC.browser.isChrome && DetectRTC.browser.version > 25) {
    isSCTPSupportd = true
  } else if (DetectRTC.browser.isOpera && DetectRTC.browser.version >= 11) {
    isSCTPSupportd = true
  }
  DetectRTC.isSctpDataChannelsSupported = isSCTPSupportd

  // ---------

  DetectRTC.isMobileDevice = isMobileDevice // "isMobileDevice" boolean is defined in "getBrowserInfo.js"

  // ------
  var isGetUserMediaSupported = false
  if (navigator.getUserMedia) {
    isGetUserMediaSupported = true
  } else if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
    isGetUserMediaSupported = true
  }

  if (DetectRTC.browser.isChrome && DetectRTC.browser.version >= 46 && !/^(https:|chrome-extension:)$/g.test(location.protocol ||
			'')) {
    if (typeof document !== 'undefined' && typeof document.domain === 'string' && document.domain.search && document.domain
      .search(/localhost|127.0./g) === -1) {
      isGetUserMediaSupported = 'Requires HTTPs'
    }
  }

  if (DetectRTC.osName === 'Nodejs') {
    isGetUserMediaSupported = false
  }
  DetectRTC.isGetUserMediaSupported = isGetUserMediaSupported

  var displayResolution = ''
  if (screen.width) {
    var width = (screen.width) ? screen.width : ''
    var height = (screen.height) ? screen.height : ''
    displayResolution += '' + width + ' x ' + height
  }
  DetectRTC.displayResolution = displayResolution

  function getAspectRatio(w, h) {
    function gcd(a, b) {
      return (b === 0) ? a : gcd(b, a % b)
    }
    var r = gcd(w, h)
    return (w / r) / (h / r)
  }

  DetectRTC.displayAspectRatio = getAspectRatio(screen.width, screen.height).toFixed(2)

  // ----------
  DetectRTC.isCanvasSupportsStreamCapturing = isCanvasSupportsStreamCapturing
  DetectRTC.isVideoSupportsStreamCapturing = isVideoSupportsStreamCapturing

  if (DetectRTC.browser.name === 'Chrome' && DetectRTC.browser.version >= 53) {
    if (!DetectRTC.isCanvasSupportsStreamCapturing) {
      DetectRTC.isCanvasSupportsStreamCapturing = 'Requires chrome flag: enable-experimental-web-platform-features'
    }

    if (!DetectRTC.isVideoSupportsStreamCapturing) {
      DetectRTC.isVideoSupportsStreamCapturing = 'Requires chrome flag: enable-experimental-web-platform-features'
    }
  }

  // ------
  DetectRTC.DetectLocalIPAddress = DetectLocalIPAddress

  DetectRTC.isWebSocketsSupported = 'WebSocket' in window && window.WebSocket.CLOSING === 2
  DetectRTC.isWebSocketsBlocked = !DetectRTC.isWebSocketsSupported

  if (DetectRTC.osName === 'Nodejs') {
    DetectRTC.isWebSocketsSupported = true
    DetectRTC.isWebSocketsBlocked = false
  }

  DetectRTC.checkWebSocketsSupport = function(callback) {
    callback = callback || function() {}
    try {
      var starttime
      var websocket = new WebSocket('wss://echo.websocket.org:443/')
      websocket.onopen = function() {
        DetectRTC.isWebSocketsBlocked = false
        starttime = (new Date()).getTime()
        websocket.send('ping')
      }
      websocket.onmessage = function() {
        DetectRTC.WebsocketLatency = (new Date()).getTime() - starttime + 'ms'
        callback()
        websocket.close()
        websocket = null
      }
      websocket.onerror = function() {
        DetectRTC.isWebSocketsBlocked = true
        callback()
      }
    } catch (e) {
      DetectRTC.isWebSocketsBlocked = true
      callback()
    }
  }

  // -------
  DetectRTC.load = function(callback) {
    callback = callback || function() {}
    checkDeviceSupport(callback)
  }

  // check for microphone/camera support!
  if (typeof checkDeviceSupport === 'function') {
    // checkDeviceSupport();
  }

  if (typeof MediaDevices !== 'undefined') {
    DetectRTC.MediaDevices = MediaDevices
  } else {
    DetectRTC.MediaDevices = []
  }

  DetectRTC.hasMicrophone = hasMicrophone
  DetectRTC.hasSpeakers = hasSpeakers
  DetectRTC.hasWebcam = hasWebcam

  DetectRTC.isWebsiteHasWebcamPermissions = isWebsiteHasWebcamPermissions
  DetectRTC.isWebsiteHasMicrophonePermissions = isWebsiteHasMicrophonePermissions

  DetectRTC.audioInputDevices = audioInputDevices
  DetectRTC.audioOutputDevices = audioOutputDevices
  DetectRTC.videoInputDevices = videoInputDevices

  // ------
  var isSetSinkIdSupported = false
  if (typeof document !== 'undefined' && typeof document.createElement === 'function' && 'setSinkId' in document.createElement(
    'video')) {
    isSetSinkIdSupported = true
  }
  DetectRTC.isSetSinkIdSupported = isSetSinkIdSupported

  // -----
  var isRTPSenderReplaceTracksSupported = false
  if (DetectRTC.browser.isFirefox && typeof mozRTCPeerConnection !== 'undefined' /* && DetectRTC.browser.version > 39*/) {
    /*global mozRTCPeerConnection:true */
    if ('getSenders' in mozRTCPeerConnection.prototype) {
      isRTPSenderReplaceTracksSupported = true
    }
  } else if (DetectRTC.browser.isChrome && typeof webkitRTCPeerConnection !== 'undefined') {
    /*global webkitRTCPeerConnection:true */
    if ('getSenders' in webkitRTCPeerConnection.prototype) {
      isRTPSenderReplaceTracksSupported = true
    }
  }
  DetectRTC.isRTPSenderReplaceTracksSupported = isRTPSenderReplaceTracksSupported

  // ------
  var isRemoteStreamProcessingSupported = false
  if (DetectRTC.browser.isFirefox && DetectRTC.browser.version > 38) {
    isRemoteStreamProcessingSupported = true
  }
  DetectRTC.isRemoteStreamProcessingSupported = isRemoteStreamProcessingSupported

  // -------
  var isApplyConstraintsSupported = false

  if (typeof MediaStreamTrack !== 'undefined' && 'applyConstraints' in MediaStreamTrack.prototype) {
    isApplyConstraintsSupported = true
  }
  DetectRTC.isApplyConstraintsSupported = isApplyConstraintsSupported

  // -------
  var isMultiMonitorScreenCapturingSupported = false
  if (DetectRTC.browser.isFirefox && DetectRTC.browser.version >= 43) {
    // version 43 merely supports platforms for multi-monitors
    // version 44 will support exact multi-monitor selection i.e. you can select any monitor for screen capturing.
    isMultiMonitorScreenCapturingSupported = true
  }
  DetectRTC.isMultiMonitorScreenCapturingSupported = isMultiMonitorScreenCapturingSupported

  DetectRTC.isPromisesSupported = !!('Promise' in window)

  // version is generated by "grunt"
  DetectRTC.version = '1.4.0'

  if (typeof DetectRTC === 'undefined') {
    window.DetectRTC = {}
  }

  var MediaStream = window.MediaStream

  if (typeof MediaStream === 'undefined' && typeof window.webkitMediaStream !== 'undefined') {
    MediaStream = window.webkitMediaStream
  }

  if (typeof MediaStream !== 'undefined' && typeof MediaStream === 'function') {
    DetectRTC.MediaStream = Object.keys(MediaStream.prototype)
  } else DetectRTC.MediaStream = false

  if (typeof MediaStreamTrack !== 'undefined') {
    DetectRTC.MediaStreamTrack = Object.keys(MediaStreamTrack.prototype)
  } else DetectRTC.MediaStreamTrack = false

  var RTCPeerConnection = window.RTCPeerConnection || window.mozRTCPeerConnection || window.webkitRTCPeerConnection

  if (typeof RTCPeerConnection !== 'undefined') {
    DetectRTC.RTCPeerConnection = Object.keys(RTCPeerConnection.prototype)
  } else DetectRTC.RTCPeerConnection = false

  window.DetectRTC = DetectRTC

  if (typeof module !== 'undefined' /* && !!module.exports*/) {
    module.exports = DetectRTC
  }

  if (typeof window.define === 'function' && window.define.amd) {
    window.define('DetectRTC', [], function() {
      return DetectRTC
    })
  }
})()
